#ifndef RAPIDXML_PRINT_HPP_INCLUDED
#define RAPIDXML_PRINT_HPP_INCLUDED

// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation

#include "rapidxml.hpp"

// Only include streams if not disabled
#ifndef RAPIDXML_NO_STREAMS
#include <ostream>
#include <iterator>
#endif

namespace behaviac
{
	namespace rapidxml
	{
		///////////////////////////////////////////////////////////////////////
		// Printing flags

		const int print_no_indenting = 0x1;   //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.

		///////////////////////////////////////////////////////////////////////
		// Internal

		//! \cond internal
		namespace internal
		{
			///////////////////////////////////////////////////////////////////////////
			// Internal character operations

			// Copy characters from given range to given output iterator
			template<class OutIt, class Ch>
			inline OutIt copy_chars(const Ch* begin, const Ch* end, OutIt out)
			{
				while (begin != end)
				{
					*out++ = *begin++;
				}

				return out;
			}

			// Copy characters from given range to given output iterator and expand
			// characters into references (&lt; &gt; &apos; &quot; &amp;)
			template<class OutIt, class Ch>
			inline OutIt copy_and_expand_chars(const Ch* begin, const Ch* end, Ch noexpand, OutIt out)
			{
				while (begin != end)
				{
					if (*begin == noexpand)
					{
						*out++ = *begin;    // No expansion, copy character
					}
					else
					{
						switch (*begin)
						{
						case Ch('<'):
							*out++ = Ch('&');
							*out++ = Ch('l');
							*out++ = Ch('t');
							*out++ = Ch(';');
							break;

						case Ch('>'):
							*out++ = Ch('&');
							*out++ = Ch('g');
							*out++ = Ch('t');
							*out++ = Ch(';');
							break;

						case Ch('\''):
							*out++ = Ch('&');
							*out++ = Ch('a');
							*out++ = Ch('p');
							*out++ = Ch('o');
							*out++ = Ch('s');
							*out++ = Ch(';');
							break;

						case Ch('"'):
							*out++ = Ch('&');
							*out++ = Ch('q');
							*out++ = Ch('u');
							*out++ = Ch('o');
							*out++ = Ch('t');
							*out++ = Ch(';');
							break;

						case Ch('&'):
							*out++ = Ch('&');
							*out++ = Ch('a');
							*out++ = Ch('m');
							*out++ = Ch('p');
							*out++ = Ch(';');
							break;

						default:
							*out++ = *begin;    // No expansion, copy character
						}
					}

					++begin;    // Step to next character
				}

				return out;
			}

			// Fill given output iterator with repetitions of the same character
			template<class OutIt, class Ch>
			inline OutIt fill_chars(OutIt out, int n, Ch ch)
			{
				for (int i = 0; i < n; ++i)
				{
					*out++ = ch;
				}

				return out;
			}

			// Find character
			template<class Ch, Ch ch>
			inline bool find_char(const Ch* begin, const Ch* end)
			{
				while (begin != end)
					if (*begin++ == ch)
					{
						return true;
					}

				return false;
			}

			///////////////////////////////////////////////////////////////////////////
			// Internal printing operations

			// Print node
			template<class OutIt, class Ch>
			inline OutIt print_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				// Print proper node type
				switch (node->type())
				{
					// Document
				case node_document:
					out = print_children(out, node, flags, indent);
					break;

					// Element
				case node_element:
					out = print_element_node(out, node, flags, indent);
					break;

					// Data
				case node_data:
					out = print_data_node(out, node, flags, indent);
					break;

					// CDATA
				case node_cdata:
					out = print_cdata_node(out, node, flags, indent);
					break;

					// Declaration
				case node_declaration:
					out = print_declaration_node(out, node, flags, indent);
					break;

					// Comment
				case node_comment:
					out = print_comment_node(out, node, flags, indent);
					break;

					// Doctype
				case node_doctype:
					out = print_doctype_node(out, node, flags, indent);
					break;

					// Pi
				case node_pi:
					out = print_pi_node(out, node, flags, indent);
					break;

					// Unknown
				default:
					assert(0);
					break;
				}

				// If indenting not disabled, add line break after node
				if (!(flags & print_no_indenting))
				{
					*out = Ch('\n'), ++out;
				}

				// Return modified iterator
				return out;
			}

			// Print children of the node
			template<class OutIt, class Ch>
			inline OutIt print_children(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				for (xml_node<Ch>* child = node->first_node(); child; child = child->next_sibling())
				{
					out = print_node(out, child, flags, indent);
				}

				return out;
			}

			// Print attributes of the node
			template<class OutIt, class Ch>
			inline OutIt print_attributes(OutIt out, const xml_node<Ch>* node, int flags)
			{
				for (xml_attribute<Ch>* attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
				{
					if (attribute->name() && attribute->value())
					{
						// Print attribute name
						*out = Ch(' '), ++out;
						out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
						*out = Ch('='), ++out;

						// Print attribute value using appropriate quote type
						if (find_char < Ch, Ch('"') >(attribute->value(), attribute->value() + attribute->value_size()))
						{
							*out = Ch('\''), ++out;
							out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
							*out = Ch('\''), ++out;
						}
						else
						{
							*out = Ch('"'), ++out;
							out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
							*out = Ch('"'), ++out;
						}
					}
				}

				return out;
			}

			// Print data node
			template<class OutIt, class Ch>
			inline OutIt print_data_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				assert(node->type() == node_data);

				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
				return out;
			}

			// Print data node
			template<class OutIt, class Ch>
			inline OutIt print_cdata_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				assert(node->type() == node_cdata);

				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				*out = Ch('<');
				++out;
				*out = Ch('!');
				++out;
				*out = Ch('[');
				++out;
				*out = Ch('C');
				++out;
				*out = Ch('D');
				++out;
				*out = Ch('A');
				++out;
				*out = Ch('T');
				++out;
				*out = Ch('A');
				++out;
				*out = Ch('[');
				++out;
				out = copy_chars(node->value(), node->value() + node->value_size(), out);
				*out = Ch(']');
				++out;
				*out = Ch(']');
				++out;
				*out = Ch('>');
				++out;
				return out;
			}

			// Print element node
			template<class OutIt, class Ch>
			inline OutIt print_element_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				assert(node->type() == node_element);

				// Print element name and attributes, if any
				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				*out = Ch('<'), ++out;
				out = copy_chars(node->name(), node->name() + node->name_size(), out);
				out = print_attributes(out, node, flags);

				// If node is childless
				if (node->value_size() == 0 && !node->first_node())
				{
					// Print childless node tag ending
					*out = Ch('/'), ++out;
					*out = Ch('>'), ++out;
				}
				else
				{
					// Print normal node tag ending
					*out = Ch('>'), ++out;

					// Test if node contains a single data node only (and no other nodes)
					xml_node<Ch>* child = node->first_node();

					if (!child)
					{
						// If node has no children, only print its value without indenting
						out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
					}
					else if (child->next_sibling() == 0 && child->type() == node_data)
					{
						// If node has a sole data child, only print its value without indenting
						out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
					}
					else
					{
						// Print all children with full indenting
						if (!(flags & print_no_indenting))
						{
							*out = Ch('\n'), ++out;
						}

						out = print_children(out, node, flags, indent + 1);

						if (!(flags & print_no_indenting))
						{
							out = fill_chars(out, indent, Ch('\t'));
						}
					}

					// Print node end
					*out = Ch('<'), ++out;
					*out = Ch('/'), ++out;
					out = copy_chars(node->name(), node->name() + node->name_size(), out);
					*out = Ch('>'), ++out;
				}

				return out;
			}

			// Print declaration node
			template<class OutIt, class Ch>
			inline OutIt print_declaration_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				// Print declaration start
				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				*out = Ch('<'), ++out;
				*out = Ch('?'), ++out;
				*out = Ch('x'), ++out;
				*out = Ch('m'), ++out;
				*out = Ch('l'), ++out;

				// Print attributes
				out = print_attributes(out, node, flags);

				// Print declaration end
				*out = Ch('?'), ++out;
				*out = Ch('>'), ++out;

				return out;
			}

			// Print comment node
			template<class OutIt, class Ch>
			inline OutIt print_comment_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				assert(node->type() == node_comment);

				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				*out = Ch('<'), ++out;
				*out = Ch('!'), ++out;
				*out = Ch('-'), ++out;
				*out = Ch('-'), ++out;
				out = copy_chars(node->value(), node->value() + node->value_size(), out);
				*out = Ch('-'), ++out;
				*out = Ch('-'), ++out;
				*out = Ch('>'), ++out;
				return out;
			}

			// Print doctype node
			template<class OutIt, class Ch>
			inline OutIt print_doctype_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				assert(node->type() == node_doctype);

				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				*out = Ch('<'), ++out;
				*out = Ch('!'), ++out;
				*out = Ch('D'), ++out;
				*out = Ch('O'), ++out;
				*out = Ch('C'), ++out;
				*out = Ch('T'), ++out;
				*out = Ch('Y'), ++out;
				*out = Ch('P'), ++out;
				*out = Ch('E'), ++out;
				*out = Ch(' '), ++out;
				out = copy_chars(node->value(), node->value() + node->value_size(), out);
				*out = Ch('>'), ++out;
				return out;
			}

			// Print pi node
			template<class OutIt, class Ch>
			inline OutIt print_pi_node(OutIt out, const xml_node<Ch>* node, int flags, int indent)
			{
				assert(node->type() == node_pi);

				if (!(flags & print_no_indenting))
				{
					out = fill_chars(out, indent, Ch('\t'));
				}

				*out = Ch('<'), ++out;
				*out = Ch('?'), ++out;
				out = copy_chars(node->name(), node->name() + node->name_size(), out);
				*out = Ch(' '), ++out;
				out = copy_chars(node->value(), node->value() + node->value_size(), out);
				*out = Ch('?'), ++out;
				*out = Ch('>'), ++out;
				return out;
			}
		}
		//! \endcond

		///////////////////////////////////////////////////////////////////////////
		// Printing

		//! Prints XML to given output iterator.
		//! \param out Output iterator to print to.
		//! \param node Node to be printed. Pass xml_document to print entire document.
		//! \param flags Flags controlling how XML is printed.
		//! \return Output iterator pointing to position immediately after last character of printed text.
		template<class OutIt, class Ch>
		inline OutIt print(OutIt out, const xml_node<Ch>& node, int flags = 0)
		{
			return internal::print_node(out, &node, flags, 0);
		}

#ifndef RAPIDXML_NO_STREAMS

		//! Prints XML to given output stream.
		//! \param out Output stream to print to.
		//! \param node Node to be printed. Pass xml_document to print entire document.
		//! \param flags Flags controlling how XML is printed.
		//! \return Output stream.
		template<class Ch>
		inline std::basic_ostream<Ch>& print(std::basic_ostream<Ch>& out, const xml_node<Ch>& node, int flags = 0)
		{
			print(std::ostream_iterator<Ch>(out), node, flags);
			return out;
		}

		//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
		//! \param out Output stream to print to.
		//! \param node Node to be printed.
		//! \return Output stream.
		template<class Ch>
		inline std::basic_ostream<Ch>& operator <<(std::basic_ostream<Ch>& out, const xml_node<Ch>& node)
		{
			return print(out, node);
		}

#endif
	}
}

#endif
